<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta
    name="viewport"
    content="width=device-width, initial-scale=1.0"
  >
  <title>虚拟滚动示例</title>
  <style>
    .container {
      width: 600px;
      /* 容器宽度 */
      height: 400px;
      /* 容器高度 */
      overflow: auto;
      /* 允许滚动 */
      position: relative;
    }

    .phantom {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      /* 占位元素宽度 */
    }

    .visible-items {
      position: absolute;
      top: 0;
      left: 0;
      display: flex;
      /* 横向排列 */
    }

    .item {
      width: 100px;
      /* 每个项目宽度 */
      height: 50px;
      /* 每个项目高度 */
      border: 1px solid #ccc;
      box-sizing: border-box;
      display: flex;
      align-items: center;
      justify-content: center;
    }
  </style>
</head>

<body>

  <div
    class="container"
    id="scrollContainer"
  >
    <div
      class="phantom"
      id="phantom"
    ></div>
    <div
      class="visible-items"
      id="visibleItems"
    ></div>
  </div>

  <script>
    const itemHeight = 50; // 每个项目高度
    const itemWidth = 100; // 每个项目宽度
    const totalItems = 1000; // 总项目数量
    const container = document.getElementById('scrollContainer');
    const phantom = document.getElementById('phantom');
    const visibleItems = document.getElementById('visibleItems');

    // 设置占位元素的高度和宽度
    phantom.style.height = (itemHeight * totalItems) + 'px';
    phantom.style.width = (itemWidth * totalItems) + 'px';

    function renderVisibleItems() {
      const scrollTop = container.scrollTop;
      const scrollLeft = container.scrollLeft;

      // 计算可见区域的起始和结束索引
      const startRowIndex = Math.floor(scrollTop / itemHeight);
      const endRowIndex = Math.min(totalItems, Math.ceil((scrollTop + container.clientHeight) / itemHeight));

      const startColIndex = Math.floor(scrollLeft / itemWidth);
      const endColIndex = Math.min(totalItems, Math.ceil((scrollLeft + container.clientWidth) / itemWidth));

      // 清空当前可见项目
      visibleItems.innerHTML = '';

      // 渲染可见项目
      for (let rowIndex = startRowIndex; rowIndex < endRowIndex; rowIndex++) {
        for (let colIndex = startColIndex; colIndex < endColIndex; colIndex++) {
          const item = document.createElement('div');
          item.className = 'item';
          item.textContent = `Item ${rowIndex * totalItems + colIndex}`;
          visibleItems.appendChild(item);
        }
      }

      // 更新可见项目的位置
      visibleItems.style.transform = `translateY(${startRowIndex * itemHeight}px) translateX(${startColIndex * itemWidth}px)`;
    }

    container.addEventListener('scroll', renderVisibleItems);

    // 初始渲染
    renderVisibleItems();
  </script>

</body>

</html>